﻿using Autofac;
using Autofac.Builder;
using Autofac.Core;
using Autofac.Features.OpenGenerics;
using AutoRegistDependency.Attributes;
using AutoRegistDependency.Component;
using AutoRegistDependency.Implement;
using Castle.DynamicProxy;
using Microsoft.Extensions.DependencyModel;
using Quartz.Util;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Loader;
using System.Threading.Tasks;
using System.Xml.Linq;

namespace AutoRegistDependency.Utils
{
    internal class AutoFacHelper
    {
        private static readonly ProxyGenerator Generator = new ProxyGenerator();
        /// <summary>
        /// 获取系统所有的dll
        /// </summary>
        /// <returns></returns>
        public static List<Assembly> GetAssemblies()
        {
            var scanAssemblies = DependencyContext.Default?.CompileLibraries
              .Where(u => (u.Type == "project" && !(u.Type == "package" && u.Type == "reference")))
              .Select(u => AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(u.Name)));
            return scanAssemblies?.ToList();
        }
        public static void SetValue(object instance, Type instanceType, FieldInfo fieldInfo, object value)
        {
            if (!instanceType.IsGenericType)
            {
                fieldInfo.SetValue(instance, value);
            }
            else
            {
                instanceType.GetTypeInfo().GetFields(BindingFlags.Public |
                BindingFlags.NonPublic |
                BindingFlags.Static |
                BindingFlags.Instance |
                BindingFlags.DeclaredOnly).Where(t => t.Name == fieldInfo.Name)?.FirstOrDefault()?.SetValue(instance, value);
            }
        }

        public static void SetValue(object instance, Type instanceType, PropertyInfo property, object value)
        {
            if (!instanceType.IsGenericType)
            {
                property.SetValue(instance, value);
            }
            else
            {
                instanceType.GetTypeInfo().GetProperties(BindingFlags.Public |
                BindingFlags.NonPublic |
                BindingFlags.Static |
                BindingFlags.Instance |
                BindingFlags.DeclaredOnly).Where(t => t.Name == property.Name)?.FirstOrDefault()?.SetValue(instance, value);
            }
        }
        /// <summary>
        /// 获取参数的默认值
        /// </summary>
        /// <param name="parameter"></param>
        /// <returns></returns>
        public static object GetParameterDefaultValue(ParameterInfo parameter)
        {
            if (!parameter.HasDefaultValue)
            {
                return GetDefaultValue(parameter.ParameterType);
            }
            else
            {
                return parameter.DefaultValue;
            }
        }

        /// <summary>
        /// 获取type的默认值
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static object GetDefaultValue(Type type)
        {
            if (Nullable.GetUnderlyingType(type) != null)
            {
                return null;
            }
            return type.IsValueType ? Activator.CreateInstance(type) : null;
        }
        /// <summary>
        /// 使用IComponentContext解析方法参数并执行方法
        /// </summary>
        /// <param name="instance">实例</param>
        /// <param name="method">被执行的方法</param>
        /// <param name="context">组件上下文</param>
        /// <param name="parameters">解析参数</param>
        /// <param name="isAsync">是否异步方法 默认false</param>
        /// <returns></returns>
        public static object InvokeMethod(object instance, MethodInfo method, IComponentContext context,IEnumerable<Parameter> parameters,bool isAsync=false)
        {
            var realInstance = GetRealInstance(instance);
            var paramters = method.GetParameters();
            var invokeParamters = new object[paramters.Length];
            for (int i = 0; i < paramters.Length; i++)
            {
                var paramter = paramters[i];
                invokeParamters[i] = GetParameterDefaultValue(paramter);
                var componentFilterAttribute = paramter.GetCustomAttribute<ComponentFilterAttribute>();
                var valueAttribute = paramter.GetCustomAttribute<ValueAttribute>();
                if (componentFilterAttribute != null)
                {
                    if (componentFilterAttribute.CanResolveParameter(paramter, context))
                    {
                        var componentService = componentFilterAttribute.ResolveParameter(paramter, context);
                        invokeParamters[i] = componentService;
                        continue;
                    }
                }
                if (valueAttribute != null)
                {
                    object configurationValue = valueAttribute.ResolveParameter(paramter, context);
                    invokeParamters[i] = configurationValue;
                    continue;
                }
                if (parameters != null)
                {
                    foreach (var param in parameters)
                    {
                        if (param.CanSupplyValue(paramter, context, out var func))
                        {
                            invokeParamters[i] = func.Invoke();
                            break;
                        }
                    }
                    continue;
                }
                if (context.TryResolve(paramter.ParameterType, out object service))
                {
                    invokeParamters[i] = service;

                }
            }
            object methodResult = method.Invoke(realInstance, invokeParamters);
            if (!isAsync)
            {
                return methodResult;
            }
            else
            {
                var awaiter = methodResult.GetType().GetMethod(nameof(Task.GetAwaiter))?.Invoke(methodResult, null);
                return awaiter?.GetType().GetMethod(nameof(TaskAwaiter.GetResult))?.Invoke(awaiter, null);
            }
        }
        /// <summary>
        /// 获取代理类的真实实例
        /// </summary>
        /// <param name="instance"></param>
        /// <returns></returns>
        public static object GetRealInstance(object instance)
        {
            if (instance == null) return null;
            var realInstance = instance;
            if (ProxyUtil.IsProxy(instance))
            {
                realInstance = ProxyUtil.GetUnproxiedInstance(instance);
            }
            return realInstance;
        }
        /// <summary>
        /// 获取被实现的接口(泛型类只会找和其参数相同的接口)
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static List<Type> GetImplInterfaces(Type type)
        {
            List<Type> interfaces = new List<Type>();
            var typeInterfaces = type.GetInterfaces().Where(t => t != typeof(IDisposable));
            //泛型类只找泛型参数相同的接口注册
            if (type.IsGenericType)
            {
                typeInterfaces = typeInterfaces.Where(t => t.IsGenericType && GetGenericTypeNumber(t) == GetGenericTypeNumber(type)).ToArray();
            }
            foreach (var typeInterface in typeInterfaces)
            {
                if (!type.IsGenericType)
                {
                    interfaces.Add(typeInterface);
                }
                else
                {
                    interfaces.Add(typeInterface.GetGenericTypeDefinition());
                }
            }
            return interfaces;
        }
        public static int GetGenericTypeNumber(Type type)
        {
            if (!type.IsGenericType)
            {
                throw new ArgumentException("非泛型类禁止调用！");
            }
            else
            {
                TypeInfo typeInfo = type.GetTypeInfo();
                return typeInfo.IsGenericTypeDefinition ? typeInfo.GenericTypeParameters.Length : typeInfo.GenericTypeArguments.Length;
            }
        }
        public static List<Type> GetGenericTypes(Type type)
        {
            TypeInfo typeInfo = type.GetTypeInfo();
            return typeInfo.IsGenericTypeDefinition ? typeInfo.GenericTypeParameters.ToList() : typeInfo.GenericTypeArguments.ToList();
        }

        public static Type GetGenericType(List<Type> genericTypes, params Type[] lastChildrenGenericType)
        {
            if (!genericTypes.Any())
            {
                throw new ArgumentException($"genericTypes can not be null!");
            }
            var lastGenericType = genericTypes.Last();
            if (genericTypes.Any(t => !t.IsGenericType))
            {
                throw new ArgumentException($"genericTypes need all type is GenericType!!");
            }
            Type result = lastGenericType.MakeGenericType(lastChildrenGenericType);
            for (int i = genericTypes.Count() - 2; i >= 0; i--)
            {

                result = genericTypes[i].MakeGenericType(result);
            }
            return result;
        }
        /// <summary>
        /// 生成代理类
        /// </summary>
        /// <param name="interfaceType"></param>
        /// <param name="componentInfo"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentException"></exception>
        public static object CreateInstance(Type interfaceType, ComponentDefinition componentInfo, IComponentContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (interfaceType == null)
            {
                throw new ArgumentNullException(nameof(interfaceType));
            }

            if (!interfaceType.GetTypeInfo().IsInterface)
            {
                throw new ArgumentException($"{interfaceType} must be interface!!");
            }
            IInterceptor resolverInterceptor = new AggregateServiceInterceptor(context, interfaceType, componentInfo);
            return Generator.CreateInterfaceProxyWithoutTarget(interfaceType, resolverInterceptor);
        }
        /// <summary>
        /// 根据代理类和接口类型生成代理实例
        /// </summary>
        /// <typeparam name="T">代理类型</typeparam>
        /// <param name="interceptor">代理类实例</param>
        /// <param name="interfaceType">接口类型</param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public static object CreateProxyInstance<T>(T interceptor, Type interfaceType) where T : IInterceptor
        {
            if (!interfaceType.GetTypeInfo().IsInterface)
            {
                throw new ArgumentException($"{interfaceType} must be interface!!");
            }
            return Generator.CreateInterfaceProxyWithoutTarget(interfaceType, interceptor);
        }

        public static IEnumerable<T> GetAttributeInstance<T>(Type type)
        {
            return type.GetCustomAttributes().Where(t => typeof(T).IsAssignableFrom(t.GetType())).Cast<T>();
        }

        public static IEnumerable<T> GetAttributeInstance<T>(MethodInfo methodInfo)
        {
            return methodInfo.GetCustomAttributes().Where(t => typeof(T).IsAssignableFrom(t.GetType())).Cast<T>();
        }
        /// <summary>
        /// 根据参数最少的构造函数实例化一个对象
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static object CreateInstance(Type type)
        {
            var ctor= type.GetConstructors().OrderBy(t=> t.GetParameters().Count()).First();
            var p = ctor.GetParameters();
            if(p.Length == 0)
            {
                return ctor.Invoke(null);
            }
            else
            {
                object[] parameters=new object[p.Length];
                for(int i = 0;i < p.Length; i++)
                {
                    parameters[i] = GetParameterDefaultValue(p[i]);
                }
                return ctor.Invoke(parameters);
            }
        }
        /// <summary>
        /// 获取解析的serviceKey
        /// </summary>
        /// <param name="name">组件name</param>
        /// <param name="key">组件key</param>
        /// <param name="type">组件类型</param>
        /// <returns></returns>
        public static Service GetResolveServiceKey(string name,object key,Type type)
        {
            Service service = null;
            if (!string.IsNullOrEmpty(name))
            {
                service = new KeyedService(name, type);
            }
            else if (key != null)
            {
                service = new KeyedService(key, type);
            }
            else
            {
                service = new TypedService(type);
            }
            return service;
        }
    }
}
